/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 *
 */

import { FileSet, MetricLogType } from '@ohos/hvigor';
import { TaskInputValue } from '@ohos/hvigor';
import * as fs from 'fs-extra';
import path from 'path';

import { NpmCommandBuilder } from '../../builder/npm-command-builder.js';
import { LogCombineType } from '../../utils/log/log-combine-type.js';
import { execOhpmPack, getNpmPath } from '../../utils/npm-utils.js';
import { ProcessUtils } from '../../utils/process-utils.js';
import { TaskNames } from '../common/task-names.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';
import Task = TaskNames.Task;

/**
 * 调用ohpm进行shared har的打包工作
 *
 * @since 2023/1/18
 */
export class PackageSharedHar extends OhosHapTask {
  // inputs
  private readonly taskTmpDir: string;
  private readonly npmCommand: string[];
  private readonly npmPath: string;

  // outputs
  private readonly outputDir: string;

  constructor(taskService: TargetTaskService) {
    super(taskService, Task.PACKAGE_SHARED_HAR);
    this.taskTmpDir = this.getTaskTempDir(this.targetData, 'shared_har', false);
    this.npmPath = path.dirname(getNpmPath());
    this.npmCommand = new NpmCommandBuilder(this.npmPath).addAllParams(['pack']);
    this.outputDir = this.pathInfo.getModuleBuildOutputPath();
  }

  declareExecutionCommand(): string {
    return `${this.npmCommand.toString()}`;
  }

  declareExecutionEnv(): Map<string, string> {
    const executionEnv = new Map<string, string>();
    if (fs.existsSync(this.taskTmpDir)) {
      executionEnv.set('cwd', this.taskTmpDir);
    }
    return executionEnv;
  }

  declareInputs(): Map<string, TaskInputValue> {
    return super.declareInputs().set('isOhpmProject', this.isOhpmProject);
  }

  declareInputFiles(): FileSet {
    const inputFiles = super.declareInputFiles();
    if (fs.existsSync(this.taskTmpDir)) {
      inputFiles.addEntry(this.taskTmpDir, { isDirectory: true });
    }
    return inputFiles;
  }

  declareOutputFiles(): FileSet {
    return super.declareOutputFiles().addEntry(this.outputDir, { isDirectory: true });
  }

  initTaskDepends(): void {
    this.declareDepends(Task.PREPARE_SHARED_HAR_RES.name);
  }

  protected async doTaskAction(): Promise<void> {
    this.isOhpmProject ? await this.executeOhpmPack() : await this.executeNpmPack();
  }

  private async executeNpmPack() {
    const subTaskName = 'generate npm packaging command';
    const subDurationEvent = this.durationEvent.createSubEvent(subTaskName, '');
    subDurationEvent.start();

    // npm pack
    const inputData = {
      moduleName: this.moduleName,
      taskName: this.name,
      commandLine: this.npmCommand,
      commandOptions: { cwd: this.taskTmpDir },
    };

    const callback = (packageName: string) => {
      fs.copySync(this.taskTmpDir, this.outputDir, {
        filter: (src) => {
          return (!fs.lstatSync(src).isDirectory() && src.endsWith(packageName)) || src === this.taskTmpDir;
        },
      });
    };

    subDurationEvent.stop();
    subDurationEvent.setLog(subTaskName, MetricLogType.INFO);

    const subToolTaskName = 'submit npm packaging task to work pool';
    const subToolDurationEvent = this.durationEvent.createSubEvent(subToolTaskName, '');
    subToolDurationEvent.start();

    if (!new ProcessUtils(this.moduleName, this.name).submitSyncExecutionWithReturn(this, this.getWorkerPool(), 'packageHar', inputData, callback)) {
      const subExecuteTaskName = 'execute npm packaging command';
      const subExecuteDurationEvent = subToolDurationEvent.createSubEvent(subExecuteTaskName, '');
      subExecuteDurationEvent.start();

      const packageName = await new ProcessUtils(this.moduleName, this.name).execute(inputData.commandLine, inputData.commandOptions, LogCombineType.DEBUG);

      subExecuteDurationEvent.stop();
      subExecuteDurationEvent.setLog(subExecuteTaskName, MetricLogType.INFO);

      callback(packageName.stdout.toString());
    }
    subToolDurationEvent.stop();
    subToolDurationEvent.setLog(subToolTaskName, MetricLogType.INFO);
  }

  private async executeOhpmPack(): Promise<void> {
    // ohpm pack .har
    const artifactName = this.targetData.getTargetOpt().output?.artifactName;
    const outputName = artifactName ? `${artifactName}` : `${this.moduleName}`;
    await execOhpmPack(this.taskTmpDir, path.resolve(this.outputDir, `${outputName}.har`), this.durationEvent);
  }
}
